#include <node.h>
#include <node_buffer.h>
#include <v8.h>

#include <iostream>
#include <assert.h>
#include <string>

static int alive;

using namespace std;
using namespace v8;
using namespace node;

static void FreeCallback(char* data, void* hint) {
  assert(data == nullptr);
  alive--;
}

std::string HexEncode(const void* bytes, size_t size) {
  static const char kHexChars[] = "0123456789ABCDEF";

  // Each input byte creates two output hex characters.
  std::string ret(size * 2, '\0');

  for (size_t i = 0; i < size; ++i) {
    char b = reinterpret_cast<const char*>(bytes)[i];
    ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
    ret[(i * 2) + 1] = kHexChars[b & 0xf];
  }
  return ret;
}

void HexEncode(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    char* buf = node::Buffer::Data(args[0]);
    string str = HexEncode(buf, 8);

    args.GetReturnValue().Set(String::NewFromUtf8(isolate, str.c_str()));
}

void Run(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  alive++;

  {
    v8::HandleScope scope(isolate);
    v8::Local<v8::Object> buf = node::Buffer::New(
          isolate,
          nullptr,
          0,
          FreeCallback,
          nullptr).ToLocalChecked();

    char* data = node::Buffer::Data(buf);
    assert(data == nullptr);
  }

  isolate->RequestGarbageCollectionForTesting(
      v8::Isolate::kFullGarbageCollection);

  assert(alive == 0);
}

void init(v8::Local<v8::Object> exports) {
  NODE_SET_METHOD(exports, "run", Run);
  NODE_SET_METHOD(exports, "encode", HexEncode);
}

NODE_MODULE(binding, init);
